; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -reassociate -S | FileCheck %s

; Basic pattern where two contiguous i8 loads form a wider i16 load
define i16 @p0_i8_i8_i16(i8* %ptr) {
; CHECK-LABEL: @p0_i8_i8_i16(
; CHECK-NEXT:    [[I:%.*]] = getelementptr inbounds i8, i8* [[PTR:%.*]], i64 1
; CHECK-NEXT:    [[I2:%.*]] = load i8, i8* [[I]], align 1
; CHECK-NEXT:    [[I3:%.*]] = zext i8 [[I2]] to i16
; CHECK-NEXT:    [[I4:%.*]] = shl i16 [[I3]], 8
; CHECK-NEXT:    [[I5:%.*]] = load i8, i8* [[PTR]], align 1
; CHECK-NEXT:    [[I6:%.*]] = zext i8 [[I5]] to i16
; CHECK-NEXT:    [[I7:%.*]] = or i16 [[I4]], [[I6]]
; CHECK-NEXT:    [[I8:%.*]] = add i16 [[I7]], 42
; CHECK-NEXT:    ret i16 [[I8]]
;
  %i = getelementptr inbounds i8, i8* %ptr, i64 1
  %i2 = load i8, i8* %i
  %i3 = zext i8 %i2 to i16
  %i4 = shl i16 %i3, 8
  %i5 = load i8, i8* %ptr
  %i6 = zext i8 %i5 to i16
  %i7 = or i16 %i4, %i6
  %i8 = add i16 %i7, 42
  ret i16 %i8
}

; Basic pattern where two contiguous i8 loads form a wider i16 load, with swapped endianness
define i16 @p1_i8_i8_i16_swapped(i8* %ptr) {
; CHECK-LABEL: @p1_i8_i8_i16_swapped(
; CHECK-NEXT:    [[I:%.*]] = load i8, i8* [[PTR:%.*]], align 1
; CHECK-NEXT:    [[I2:%.*]] = zext i8 [[I]] to i16
; CHECK-NEXT:    [[I3:%.*]] = shl i16 [[I2]], 8
; CHECK-NEXT:    [[I4:%.*]] = getelementptr inbounds i8, i8* [[PTR]], i64 1
; CHECK-NEXT:    [[I5:%.*]] = load i8, i8* [[I4]], align 1
; CHECK-NEXT:    [[I6:%.*]] = zext i8 [[I5]] to i16
; CHECK-NEXT:    [[I7:%.*]] = or i16 [[I3]], [[I6]]
; CHECK-NEXT:    [[I8:%.*]] = add i16 [[I7]], 42
; CHECK-NEXT:    ret i16 [[I8]]
;
  %i = load i8, i8* %ptr
  %i2 = zext i8 %i to i16
  %i3 = shl i16 %i2, 8
  %i4 = getelementptr inbounds i8, i8* %ptr, i64 1
  %i5 = load i8, i8* %i4
  %i6 = zext i8 %i5 to i16
  %i7 = or i16 %i3, %i6
  %i8 = add i16 %i7, 42
  ret i16 %i8
}

; Loads are spaced out by a bit, but we don't check for that.
define i16 @p2(i8* %ptr) {
; CHECK-LABEL: @p2(
; CHECK-NEXT:    [[I:%.*]] = getelementptr inbounds i8, i8* [[PTR:%.*]], i64 1
; CHECK-NEXT:    [[I2:%.*]] = load i8, i8* [[I]], align 1
; CHECK-NEXT:    [[I3:%.*]] = zext i8 [[I2]] to i16
; CHECK-NEXT:    [[I4:%.*]] = shl i16 [[I3]], 9
; CHECK-NEXT:    [[I5:%.*]] = load i8, i8* [[PTR]], align 1
; CHECK-NEXT:    [[I6:%.*]] = zext i8 [[I5]] to i16
; CHECK-NEXT:    [[I7:%.*]] = or i16 [[I4]], [[I6]]
; CHECK-NEXT:    [[I8:%.*]] = add i16 [[I7]], 42
; CHECK-NEXT:    ret i16 [[I8]]
;
  %i = getelementptr inbounds i8, i8* %ptr, i64 1
  %i2 = load i8, i8* %i
  %i3 = zext i8 %i2 to i16
  %i4 = shl i16 %i3, 9 ; wrong shift amount
  %i5 = load i8, i8* %ptr
  %i6 = zext i8 %i5 to i16
  %i7 = or i16 %i4, %i6
  %i8 = add i16 %i7, 42
  ret i16 %i8
}

; Both bytes are the same, but we don't check for that.
define i16 @p3(i8* %ptr) {
; CHECK-LABEL: @p3(
; CHECK-NEXT:    [[I:%.*]] = load i8, i8* [[PTR:%.*]], align 1
; CHECK-NEXT:    [[I2:%.*]] = zext i8 [[I]] to i16
; CHECK-NEXT:    [[I3:%.*]] = shl i16 [[I2]], 8
; CHECK-NEXT:    [[I4:%.*]] = or i16 [[I3]], [[I2]]
; CHECK-NEXT:    [[I5:%.*]] = add i16 [[I4]], 42
; CHECK-NEXT:    ret i16 [[I5]]
;
  %i = load i8, i8* %ptr
  %i2 = zext i8 %i to i16
  %i3 = shl i16 %i2, 8
  %i4 = or i16 %i3, %i2
  %i5 = add i16 %i4, 42
  ret i16 %i5
}

; ---------------------------------------------------------------------------- ;
; Negative tests, should be transformed.

; Low bits are not a load
define i16 @n4(i8* %ptr) {
; CHECK-LABEL: @n4(
; CHECK-NEXT:    [[I:%.*]] = load i8, i8* [[PTR:%.*]], align 1
; CHECK-NEXT:    [[I2:%.*]] = zext i8 [[I]] to i16
; CHECK-NEXT:    [[I3:%.*]] = shl i16 [[I2]], 8
; CHECK-NEXT:    [[I5:%.*]] = add i16 [[I3]], 84
; CHECK-NEXT:    ret i16 [[I5]]
;
  %i = load i8, i8* %ptr
  %i2 = zext i8 %i to i16
  %i3 = shl i16 %i2, 8
  %i4 = or i16 %i3, 42 ; Second operand is bad
  %i5 = add i16 %i4, 42
  ret i16 %i5
}

; Low bits are not a load
define i16 @n5(i8* %ptr, i8 %lowbits) {
; CHECK-LABEL: @n5(
; CHECK-NEXT:    [[I:%.*]] = load i8, i8* [[PTR:%.*]], align 1
; CHECK-NEXT:    [[I2:%.*]] = zext i8 [[I]] to i16
; CHECK-NEXT:    [[I3:%.*]] = shl i16 [[I2]], 8
; CHECK-NEXT:    [[I4:%.*]] = zext i8 [[LOWBITS:%.*]] to i16
; CHECK-NEXT:    [[I5:%.*]] = add i16 [[I4]], 42
; CHECK-NEXT:    [[I6:%.*]] = add i16 [[I5]], [[I3]]
; CHECK-NEXT:    ret i16 [[I6]]
;
  %i = load i8, i8* %ptr
  %i2 = zext i8 %i to i16
  %i3 = shl i16 %i2, 8
  %i4 = zext i8 %lowbits to i16 ; base operand is bad
  %i5 = or i16 %i3, %i4
  %i6 = add i16 %i5, 42
  ret i16 %i6
}

; High bits are not a load
define i16 @n6(i8* %ptr, i8 %highbits) {
; CHECK-LABEL: @n6(
; CHECK-NEXT:    [[I:%.*]] = getelementptr inbounds i8, i8* [[PTR:%.*]], i64 1
; CHECK-NEXT:    [[I4:%.*]] = shl i16 42, 8
; CHECK-NEXT:    [[I5:%.*]] = load i8, i8* [[PTR]], align 1
; CHECK-NEXT:    [[I6:%.*]] = zext i8 [[I5]] to i16
; CHECK-NEXT:    [[I7:%.*]] = add i16 [[I4]], 42
; CHECK-NEXT:    [[I8:%.*]] = add i16 [[I7]], [[I6]]
; CHECK-NEXT:    ret i16 [[I8]]
;
  %i = getelementptr inbounds i8, i8* %ptr, i64 1
  %i2 = load i8, i8* %i
  %i4 = shl i16 42, 8 ; base operand is bad
  %i5 = load i8, i8* %ptr
  %i6 = zext i8 %i5 to i16
  %i7 = or i16 %i4, %i6
  %i8 = add i16 %i7, 42
  ret i16 %i8
}
